package com.framework.file.controller;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.framework.common.domain.ResultData;
import com.framework.common.exception.BusinessException;
import com.framework.common.util.RedisUtil;
import com.framework.file.dto.SysFileDto;
import com.framework.file.model.SysFile;
import com.framework.file.param.SysFileParam;
import com.framework.file.service.ISysFileService;
import com.framework.file.vo.ChunkMergeVo;
import com.framework.file.vo.ChunkVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Log4j2
@Api(tags = {"文件接口"})
@RestController
@CrossOrigin(origins = "*", maxAge = 3600)  //跨域请求
@RequestMapping(value = {"file"})
public class SysFileController {
    @Autowired
    private ISysFileService sysFileService;
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 上传单个文件
     * @return
     */
    @ApiOperation("上传单个文件")
    @PostMapping(value = "/upload")
    public ResultData upload(@RequestPart(value = "file") MultipartFile file,
                             @RequestParam String module,
                             @RequestParam(required = false) String form,
                             @RequestParam String part,
                             @RequestParam String createrId) throws Exception {
        if (file.getSize() <= 0) {
            throw new BusinessException("文件大小不能为0");
        }
        SysFileDto sysFileDto = SysFileDto.builder();
        sysFileDto.setName(file.getOriginalFilename());
        sysFileDto.setExt(StringUtils.getFilenameExtension(file.getOriginalFilename()));
        sysFileDto.setSize(file.getSize());
        sysFileDto.setAuthor(createrId);
        sysFileDto.setModule(module);
        sysFileDto.setPart(part);
        sysFileDto.setForm(form);
        sysFileDto.setMultipartFile(file);
        String fileName = StringUtils.getFilename(file.getOriginalFilename());
        fileName = fileName.substring(fileName.lastIndexOf(46));
        sysFileDto.setName(fileName);

        String id = sysFileService.upload(sysFileDto);
        String previewUrl = sysFileService.preview(id);

        Map<String, String> paramMap = Maps.newHashMap();
        paramMap.put("id", id);
        paramMap.put("url", previewUrl);
        return ResultData.data(paramMap);
    }

    /**
     * 批量上传文件
     *
     * @param files
     * @param module
     * @param form
     * @param part
     * @return
     */
    @ApiOperation("批量上传文件")
    @PostMapping(value = "/uploadBatch")
    public ResultData uploadBatch(@RequestPart(value = "files") List<MultipartFile> files,
                                  @RequestParam String module,
                                  @RequestParam(required = false) String form,
                                  @RequestParam String part,
                                  @RequestParam String createrId) throws Exception {
        List<String> ids = Lists.newArrayList();
        for (MultipartFile file : files) {
            SysFileDto sysFileDto = SysFileDto.builder();
            sysFileDto.setName(file.getOriginalFilename());
            sysFileDto.setExt(StringUtils.getFilenameExtension(file.getOriginalFilename()));
            sysFileDto.setSize(file.getSize());
            sysFileDto.setAuthor(createrId);
            sysFileDto.setModule(module);
            sysFileDto.setPart(part);
            sysFileDto.setForm(form);
            sysFileDto.setMultipartFile(file);

            String id = sysFileService.upload(sysFileDto);
            ids.add(id);
        }
        return ResultData.data(ids);
    }


    @ApiOperation("前端单个文件下载")
    @GetMapping(value = "/download")
    public void download(@ApiParam("文件id") @RequestParam String id) throws Exception {
        sysFileService.download(id);
    }

    @ApiOperation("后端单个文件下载")
    @GetMapping(value = "/api/download")
    public ResponseEntity<byte[]> apiDownload( @RequestParam String id) throws Exception {
        //服务提供方
        SysFileDto dto = sysFileService.getFileData(id);
        HttpHeaders httpHeaders = new HttpHeaders();
        //声明返回的类型 为一个stream流
        httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        //设置文件名称
        if (com.framework.common.util.StringUtils.isBlank(dto.getName())) {
            dto.setName("temp");
        }
        httpHeaders.setContentDispositionFormData("attachment", dto.getName().concat(".").concat(dto.getExt()));
        //大小
        httpHeaders.setContentLength(dto.getSize());

        ResponseEntity<byte[]> entity = new ResponseEntity<>(dto.getData(), httpHeaders, HttpStatus.OK);
        return entity;
    }

    @ApiOperation("文件预览(返回文件预览地址)")
    @RequestMapping(value = "/preview", method = {RequestMethod.POST, RequestMethod.GET})
    public ResultData preview(@ApiParam("文件id") @RequestParam String id) throws Exception {
        String previewUrl = sysFileService.preview(id);
        if (com.framework.common.util.StringUtils.isBlank(previewUrl)) {
            return ResultData.error("文件预览失败!");
        } else {
            return ResultData.data(previewUrl);
        }
    }

    @ApiOperation("文件预览(返回文件预览地址)批量")
    @GetMapping(value = "/previewBatch")
    public ResultData<Map<String, String>> previewBatch(@ApiParam("文件ids") @RequestParam List<String> idsList) throws Exception {
        return ResultData.data(sysFileService.previewBatch(idsList));
    }

    @ApiOperation("后端接口-上传新文件并更新旧文件信息")
    @PostMapping(value = "/apiUploadAndUpdate")
    public ResultData apiUploadAndUpdate(@RequestBody SysFileDto sysFile, @RequestParam String oldFileId) throws Exception {
        sysFileService.uploadAndUpdate(sysFile, oldFileId);
        return ResultData.success();
    }

    @ApiOperation("后端接口更新")
    @GetMapping(value = "/apiUpdateFile")
    public ResultData apiUpdateFile(@RequestBody SysFile sysFile) {
        sysFileService.save(sysFile);
        return ResultData.success();
    }

    @ApiOperation("获取文件列表")
    @RequestMapping(value = "/getFiles", method = {RequestMethod.POST, RequestMethod.GET})
    public ResultData<List<SysFile>> getFiles(@ApiParam("所属模块") @RequestParam String module,
                                              @ApiParam("所属表单") @RequestParam String form,
                                              @ApiParam("所属表单") @RequestParam(required = false) String part) {
        return ResultData.data(sysFileService.getFileByModuleAndForm(module, form, part));
    }

    @ApiOperation("获取单个文件")
    @GetMapping(value = "/getFileById")
    public ResultData getFileById(@ApiParam("文件id") @RequestParam String id) {
        SysFile sysFile = sysFileService.findOne(id);
        return ResultData.data(sysFile);
    }

    @ApiOperation("获取字段信息分页接口")
    @GetMapping(value = "/listPage")
    public ResultData listPage(@RequestBody(required = false) SysFileParam param) {
        PageRequest pageRequest = PageRequest.of(param.getPageNo(), param.getPageSize());
        Page<SysFile> users = sysFileService.listPage(param, pageRequest);
        return ResultData.data(users);
    }

    @ApiOperation("根据业务id删除文件列表")
    @PostMapping(value = "/deleteFileByForm")
    public ResultData deleteFilesByForm(@RequestParam String form, @RequestParam String part) {
        sysFileService.deleteFileByFormAndPart(form, part);
        return ResultData.success("删除成功!");
    }

    @ApiOperation("删除文件列表")
    @PostMapping(value = "/deleteFile")
    public ResultData deleteFile(@RequestParam String id) {
        sysFileService.deleteFile(id);
        return ResultData.success("删除成功!");
    }

    @ApiOperation("批量删除文件列表")
    @PostMapping(value = "/deleteFileBatch")
    public ResultData deleteFileBatch(@RequestParam List<String> ids) {
        ids.forEach(e -> {
            sysFileService.deleteFile(e);
        });
        return ResultData.success("删除成功!");
    }

    @ApiOperation("只编辑文件对象")
    @PostMapping(value = "/edit")
    public ResultData edit(@RequestBody SysFile sysFile) {
        String id = sysFileService.save(sysFile);
        return ResultData.data(id);
    }

    @ApiOperation("Vue Simple Upload 会调用这个")
    @GetMapping(value = "/chunkUpload")
    public ResultData uploadGetChunk(ChunkVo chunkVo) {
        String storeFileKey = "FILE_STORE_KEY_" + chunkVo.getIdentifier();
        String fileInfoKey = "FILE_INFO_KEY_" + chunkVo.getIdentifier();
        redisUtil.set(fileInfoKey, chunkVo);

        String chunkNumber = chunkVo.getChunkNumber();

        Object value = redisUtil.hget(storeFileKey, chunkNumber);
        // 是否跳过上传，达到断点续传功能， 这里需要检测下
        HashMap<String, Object> map = new HashMap<>();
        if (ObjectUtils.isEmpty(value)) {
            map.put("skipUpload", false);
        } else {
            map.put("skipUpload", true);
        }
        map.put("totalChunks", chunkVo.getTotalChunks());
        return ResultData.data(map);
    }

    @ApiOperation("Vue Simple Upload 上传文件")
    @PostMapping(value = "/chunkUpload")
    public ResultData uploadChunk(@RequestPart(value = "file") MultipartFile file, ChunkVo chunkVo) throws Exception {
        String chunkNumber = chunkVo.getChunkNumber();
        String identifier = chunkVo.getIdentifier();

        // 存储分片文件
        String fileName = sysFileService.uploadChunk(file, chunkVo);

        //--5 存储每一个分片的信息
        String storeFileKey = "FILE_STORE_KEY_" + identifier;
        log.info("storeFileKey="+storeFileKey);
        redisUtil.hset(storeFileKey, chunkNumber, fileName);
        redisUtil.set(identifier, fileName);

        return ResultData.data(fileName);
    }

    @ApiOperation("合并上传文件")
    @PostMapping(value = "/mergeChunks")
    public ResultData mergeChunks(@RequestBody ChunkMergeVo mergeFileVueVo) throws Exception {
        String storeFileKey = "FILE_STORE_KEY_" + mergeFileVueVo.getIdentifier();
        //--1 检查是否真的上传了所有分片
        String fileInfoKey = "FILE_INFO_KEY_" + mergeFileVueVo.getIdentifier();
        String identifier = mergeFileVueVo.getIdentifier();
        ChunkVo chunkVo = redisUtil.get(fileInfoKey);

        // 最后上传的文件名， 用来处理不分片的情况
        String fileName = redisUtil.get(identifier);
        mergeFileVueVo.setUid(fileName);


        // 文件信息丢失，重新上传
        if (ObjectUtils.isEmpty(chunkVo)) {
            redisUtil.del(fileInfoKey);
            redisUtil.del(storeFileKey);
            redisUtil.del(identifier);
            throw new Exception("文件分片信息丢失，请重新上传");
        }

        String chunks = chunkVo.getTotalChunks();


        boolean ok = true;
        for (int index = 1; index <= Integer.valueOf(chunks); index++) {
            if(!redisUtil.hHasKey(storeFileKey, String.valueOf(index))) {
                ok = false;
                break;
            }
        }

        redisUtil.del(fileInfoKey);
        redisUtil.del(storeFileKey);
        redisUtil.del(identifier);
        if(!ok) {
            throw new Exception("文件分片不全，请重新上传");
        }


        //--2 进行文件合并
        String id = sysFileService.mergeChunks(mergeFileVueVo, chunkVo);

        //--5 存入数据库
        return ResultData.success(id);
    }
}
